package org.zebra.web.advice.crypto;

import cn.hutool.extra.spring.SpringUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.zebra.common.core.BaseResponseBodyAdviceChain;
import org.zebra.common.core.Result;
import org.zebra.common.crypto.CryptoProvider;
import org.zebra.web.annotation.crypto.ApiCrypto;

import java.io.IOException;
import java.text.DateFormat;
import java.util.Date;

/**
 * @author zhanghongbin
 */
@Slf4j
public class ApiEncryptResponseBodyAdviceChain extends BaseResponseBodyAdviceChain {

    private CryptoProvider cryptoProvider;
    private ObjectMapper objectMapper;

    public ApiEncryptResponseBodyAdviceChain(CryptoProvider cryptoProvider) {
        this.cryptoProvider = cryptoProvider;
        ObjectMapper innerObjectMapper = SpringUtil.getBean(ObjectMapper.class);
        objectMapper = new ObjectMapper();
        objectMapper.setDateFormat(innerObjectMapper.getDateFormat());
        objectMapper.setConfig(innerObjectMapper.getSerializationConfig());
        objectMapper.setSerializerFactory(innerObjectMapper.getSerializerFactory());
        SimpleModule module = new SimpleModule();
        module.addSerializer(Date.class, new DateSerializer(objectMapper.getDateFormat()));
        objectMapper.registerModule(module);
    }

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(
            Object body,
            MethodParameter returnType,
            MediaType selectedContentType,
            Class<? extends HttpMessageConverter<?>> selectedConverterType,
            ServerHttpRequest request,
            ServerHttpResponse response) {
        ApiCrypto apiCrypto = returnType.getMethodAnnotation(ApiCrypto.class);
        if (apiCrypto != null && apiCrypto.response()) {
            Result<Object> result = (Result) body;
            try {
                String content;
                if (result.getData() instanceof String) {
                    content = result.getData().toString();
                } else {
                    content = objectMapper.writeValueAsString(result.getData());
                }
                result.setData(cryptoProvider.encrypt(content));
            } catch (Exception e) {
                log.error("json序列化失败", e);
            }
        }
        if (this.baseResponseBodyAdviceChain != null) {
            return this.baseResponseBodyAdviceChain.beforeBodyWrite(
                    body, returnType, selectedContentType, selectedConverterType, request, response);
        }
        return body;
    }

    private static class DateSerializer extends JsonSerializer<Date> {
        private DateFormat dateFormat;

        public DateSerializer(DateFormat dateFormat) {
            this.dateFormat = dateFormat;
        }

        @Override
        public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeRawValue(dateFormat.format(value));
        }
    }
}
